const reqlib = require('app-root-path').require
const numberUtil = reqlib('/app/util/numberUtil')
const btbAs = reqlib('/app/common/btb')

const countDifferenceRate = (numerator, denominator) => {
  if (numerator && denominator) {
    return numberUtil.countDifferenceRate(numerator, denominator) || 0
  }
  return 0
}

function CountModel (func, direction) {
  this.direction = direction || 'up'
  this.days = 0
  this.end = false
  this.func = func
  this.startKline = {}
  this.endKline = {}
  // 总近到远
  this.klineList = []
}
CountModel.prototype = {
  // 近的日子在前
  count (item, index) {
    if (!this.end) {
      if (this.func(item, index, this)) {
        if (this.days === 0) {
          this.endKline = {
            ...item
          }
        }
        this.startKline = {
          ...item
        }
        this.klineList.push(item)
        this.days++
      } else {
        this.end = true
      }
    }
  },
  // 近的日子在前
  countLast (item, index) {
    if (index === 0) {
      if (this.func(item, index, this)) {
        this.end = true
      }
    }
    if (!this.end) {
      if (this.func(item, index, this)) {
        if (this.days === 0) {
          this.endKline = {
            ...item
          }
        }
        this.startKline = {
          ...item
        }
        this.klineList.push(item)
        this.days++
      } else {
        if (this.days > 0) {
          this.end = true
        }
      }
    }
  },
  // getMaxCloseDays() {
  //   let days = 0
  // },
  countRate () {
    return countDifferenceRate(this.endKline.close, this.startKline.open)
  },
  countBuySellRate () {
    return countDifferenceRate(this.endKline.close, this.startKline.close)
  },
  // 实际涨跌幅绝对值/k涨跌幅度绝对值相加
  countRateRatioNetChangeSum () {
    if (!this.days) {
      return 0
    }
    const rate = Math.abs(countDifferenceRate(this.endKline.close, this.startKline.open))
    let sum = 0
    this.klineList.forEach((v) => {
      sum += Math.abs(v.netChangeRatio) || 0
    })
    return numberUtil.keepFourDecimals(rate / sum)
  },
  countRealMaxRateRatio () {
    if (!this.days) {
      return 0
    }
    let real = 0
    let max = 0
    this.klineList.forEach((v) => {
      real += Math.abs(v.netChangeRatio) || 0
      max += v.highLowRate || 0
    })
    return numberUtil.keepFourDecimals(real / max)
  },
  countUpDays () {
    if (!this.days) {
      return 0
    }
    let count = 0
    this.klineList.forEach((v) => {
      if (v.netChangeRatio > 0) {
        count++
      }
    })
    return count
  },
  countFirstTwoDaysRateRatio () {
    // 如果是up:如果最终rate是负数，那就返回1；
    // 如果是正数，取第一个的最高或者close，去前两个的最高或者close
    // 首先前两个rate越小越好是负数也可以
    const sumRate = this.countRate()
    if (this.days < 2 || sumRate === 0) {
      return 1
    }
    if (this.direction === 'up' && sumRate <= 0) {
      return 1
    }
    if (this.direction === 'down' && sumRate >= 0) {
      return 1
    }
    let twoDay = this.klineList[this.klineList.length - 2]
    let close = this.startKline.close
    if (this.direction === 'up') {
      if (twoDay.close > close) {
        close = twoDay.close
      }
    } else {
      // 下跌
      if (twoDay.close < close) {
        close = twoDay.close
      }
    }
    const startRate = numberUtil.countDifferenceRate(close, this.startKline.open)
    return numberUtil.keepTwoDecimals(startRate / sumRate)
  },
  countFirstTwoDaysMaxRateRatio () {
    const sumRate = this.countRate()
    if (this.days < 2 || sumRate === 0) {
      return 1
    }
    if (this.direction === 'up' && sumRate <= 0) {
      return 1
    }
    if (this.direction === 'down' && sumRate >= 0) {
      return 1
    }
    let twoDay = this.klineList[this.klineList.length - 2]
    let close = this.startKline.close
    if (this.direction === 'up') {
      close = this.startKline.high
      if (twoDay.high > close) {
        close = twoDay.high
      }
    } else {
      // 下跌
      close = this.startKline.low
      if (twoDay.low < close) {
        close = twoDay.low
      }
    }
    const startRate = numberUtil.countDifferenceRate(close, this.startKline.open)
    return numberUtil.keepTwoDecimals(startRate / sumRate)
  },
  countMaxDaysRateRatio () {
    // 如果是up:如果最终rate是负数，那就返回1；
    // 如果是正数，取第一个的最高或者close，去前两个的最高或者close
    // 最大rate占比越小越好是负数也可以
    const sumRate = this.countRate()
    if (this.days < 2 || sumRate === 0) {
      return 1
    }
    if (this.direction === 'up' && sumRate <= 0) {
      return 1
    }
    if (this.direction === 'down' && sumRate >= 0) {
      return 1
    }
    const netChangeRatioList = this.klineList.map((v) => {
      return v.netChangeRatio
    })
    if (this.direction === 'up') {
      netChangeRatioList.sort((a, b) => {
        return b - a
      })
    } else {
      // 下跌
      netChangeRatioList.sort((a, b) => {
        return a - b
      })
    }
    // 虽然取的15天，但是有一半是跌的天数
    let end = parseInt(this.klineList.length / 15)
    if (end < 1) {
      end = 1
    }
    let list = netChangeRatioList.slice(0, end)
    let rate = 0
    list.forEach((v) => {
      rate += v
    })
    let result = numberUtil.keepTwoDecimals(rate / sumRate)
    if (result > 2) {
      result = 2
    }
    return result
  },
  // 冲高回落
  countIfRiseAndFall () {
    let info = {
      flag: false,
      index: 0
    }
    this.klineList.forEach((v, index) => {
      if (btbAs.getIfRiseAndFall(v, 15) && !info.flag) {
        info.flag = true
        info.index = index
      }
    })
    return info
  },
  countMaxHighLowInfo () {
    const info = {
      high: 0,
      highIndex: 0,
      low: 0,
      lowIndex: 0,
      maxHigh: 0,
      maxHighIndex: 0,
      maxClose: 0,
      maxCloseIndex: 0,
      minClose: 0,
      minCloseIndex: 0,
      isHigherThanStart: false,
      isLowerThanStart: false,
      macdSum: 0,
      minLow: 0,
      minLowIndex: 0
    }
    this.klineList.forEach((v, index) => {
      info.macdSum += Math.abs(v.macd)
      if (v.high > info.high) {
        info.high = v.high
        info.highIndex = index
      }
      if (!info.low || v.low < info.low) {
        info.low = v.low
        info.lowIndex = index
      }
      if (v.close > info.maxClose) {
        info.maxClose = v.close
        info.maxCloseIndex = index
      }
      if (v.high > info.maxHigh) {
        info.maxHigh = v.high
        info.maxHighIndex = index
      }
      if (!info.minClose || v.close < info.minClose) {
        info.minClose = v.close
        info.minCloseIndex = index
      }
      if (!info.minLow || v.low < info.minLow) {
        info.minLow = v.low
        info.minLowIndex = index
      }
    })
    if (this.klineList[0] && this.startKline.high) {
      if (this.klineList[0].close > this.startKline.high) {
        info.isHigherThanStart = true
      }
      if (this.klineList[0].close < this.startKline.low) {
        info.isLowerThanStart = true
      }
    }
    info.macdav = info.macdSum / (this.klineList.length || 1)
    info.nowMacdRate = (this.endKline.macd || 0) / (info.macdav || 1)
    info.maxCloseRate = countDifferenceRate(info.maxClose, this.startKline.open)
    info.minCloseRate = countDifferenceRate(info.minClose, this.startKline.open)
    info.maxHighRate = countDifferenceRate(info.maxHigh, this.startKline.open)
    info.minLowRate = countDifferenceRate(info.minLow, this.startKline.open)
    info.closeLowRate = this.klineList[0] ? countDifferenceRate(this.klineList[0].close, info.minLow) : 0
    info.closeHighRate = this.klineList[0] ? countDifferenceRate(this.klineList[0].close, info.maxHigh) : 0
    return info
  },
  getSumInfo () {
    let upDays = this.countUpDays()
    const riseAndFallInfo = this.countIfRiseAndFall()
    const maxHighLowInfo = this.countMaxHighLowInfo()
    return {
      rate: this.countRate(),
      // 涨幅/总波动
      rrncs: this.countRateRatioNetChangeSum(),
      // 波动最大的几天的涨幅/总涨幅度，跌也是（越小越好）
      mdrr: this.countMaxDaysRateRatio(),
      // 上涨天数
      ud: upDays,
      // 下跌天数
      dd: this.days - upDays,
      udr: numberUtil.keepFourDecimals(upDays / this.days),
      // 实际波动之和/最大波动之和
      rmrr: this.countRealMaxRateRatio(),
      // 前两天的涨幅占实际涨幅的比例，越小越好
      ftdrr: this.countFirstTwoDaysRateRatio(),
      // 前两天的波动占实际波动的比例，越小越好
      ftdmrr: this.countFirstTwoDaysMaxRateRatio(),
      // 是否冲高回落
      rafif: riseAndFallInfo.flag,
      // 冲高回落的K线的index，index越小越靠近现在
      rafii: riseAndFallInfo.index,
      high: maxHighLowInfo.high,
      highIndex: maxHighLowInfo.highIndex,
      low: maxHighLowInfo.low,
      lowIndex: maxHighLowInfo.lowIndex,
      maxClose: maxHighLowInfo.maxClose,
      maxCloseIndex: maxHighLowInfo.maxCloseIndex,
      minClose: maxHighLowInfo.minClose,
      minCloseIndex: maxHighLowInfo.minCloseIndex,
      // macd的均值，可以看是不是一个K突然放量
      macdav: maxHighLowInfo.macdav,
      nowMacdRate: maxHighLowInfo.nowMacdRate,
      maxCloseRate: maxHighLowInfo.maxCloseRate,
      maxHighRate: maxHighLowInfo.maxHighRate,
      minCloseRate: maxHighLowInfo.minCloseRate,
      closeLowRate: maxHighLowInfo.closeLowRate,
      closeHighRate: maxHighLowInfo.closeHighRate,
      // 和第一k对比，有没有高过，有没有低过
      ihts: maxHighLowInfo.isHigherThanStart,
      ilts: maxHighLowInfo.isLowerThanStart
    }
  }
}

module.exports = (analysisInfoList) => {
  const netChangeRatioUp = new CountModel(function (item) {
    return item.netChangeRatio > 0
  }, 'up')
  const netChangeRatioDown = new CountModel(function (item) {
    return item.netChangeRatio < 0
  }, 'down')
  const kdjUp = new CountModel(function (item) {
    return item.kdjDiff > 0
  }, 'up')
  const kdjDown = new CountModel(function (item) {
    return item.kdjDiff < 0
  }, 'down')
  const macdUp = new CountModel(function (item) {
    return item.macd > 0
  }, 'up')
  const macdDown = new CountModel(function (item) {
    return item.macd < 0
  }, 'down')
  const macdIncrease = new CountModel(function (item) {
    return item.isMacdIncrease
  }, 'up')
  const macdDecline = new CountModel(function (item) {
    return item.isMacdDecline
  }, 'down')
  const ema5A10 = new CountModel(function (item) {
    return (item.ema5 > item.ema10)
  }, 'up')
  const ema5B10 = new CountModel(function (item) {
    return (item.ema5 < item.ema10)
  }, 'down')
  const ema5A20 = new CountModel(function (item) {
    return (item.ema5 > item.ema20)
  }, 'up')
  const ema5B20 = new CountModel(function (item) {
    return (item.ema5 < item.ema20)
  }, 'down')
  const ema10A20 = new CountModel(function (item) {
    return (item.ema10 > item.ema20)
  }, 'up')
  const ema10B20 = new CountModel(function (item) {
    return (item.ema10 < item.ema20)
  }, 'down')
  const ema5W10A20 = new CountModel(function (item) {
    return (item.ema10 > item.ema20 && item.ema5 > item.ema20)
  }, 'up')
  const ema5W10B20 = new CountModel(function (item) {
    return (item.ema10 < item.ema20 && item.ema5 < item.ema20)
  }, 'down')
  const ema5A10A20 = new CountModel(function (item) {
    return (item.ema10 > item.ema20 && item.ema5 > item.ema10)
  }, 'up')
  const ema5B10B20 = new CountModel(function (item) {
    return (item.ema10 < item.ema20 && item.ema5 < item.ema10)
  }, 'down')
  const lastNetChangeRatioUp = new CountModel(function (item) {
    return item.netChangeRatio > 0
  }, 'up')
  const lastNetChangeRatioDown = new CountModel(function (item) {
    return item.netChangeRatio < 0
  }, 'down')
  const lastMacdUp = new CountModel(function (item) {
    return (item.macd >= 0)
  }, 'up')
  const lastMacdDown = new CountModel(function (item) {
    return (item.macd <= 0)
  }, 'down')
  const lastKdjUp = new CountModel(function (item) {
    return (item.kdjDiff >= 0)
  }, 'up')
  const lastKdjDown = new CountModel(function (item) {
    return (item.kdjDiff <= 0)
  }, 'down')
  const lastEma5A10 = new CountModel(function (item) {
    return (item.ema5 > item.ema10)
  }, 'up')
  const lastEma5B10 = new CountModel(function (item) {
    return (item.ema5 < item.ema10)
  }, 'down')
  const lastEma5A20 = new CountModel(function (item) {
    return (item.ema5 > item.ema20)
  }, 'up')
  const lastEma5B20 = new CountModel(function (item) {
    return (item.ema5 < item.ema20)
  }, 'down')
  const lastEma5W10A20 = new CountModel(function (item) {
    return (item.ema10 > item.ema20 && item.ema5 > item.ema20)
  }, 'up')
  const lastEma5W10B20 = new CountModel(function (item) {
    return (item.ema10 < item.ema20 && item.ema5 < item.ema20)
  }, 'down')
  const lastEma5A10A20 = new CountModel(function (item) {
    return (item.ema10 > item.ema20 && item.ema5 > item.ema10)
  }, 'up')
  const lastEma5B10B20 = new CountModel(function (item) {
    return (item.ema10 < item.ema20 && item.ema5 < item.ema10)
  }, 'down')
  for (let i = 0; i < analysisInfoList.length; i++) {
    const item = analysisInfoList[i]
    if (!item) {
      break
    }
    netChangeRatioUp.count(item, i)
    netChangeRatioDown.count(item, i)
    kdjUp.count(item, i)
    kdjDown.count(item, i)
    macdUp.count(item, i)
    macdDown.count(item, i)
    macdIncrease.count(item, i)
    macdDecline.count(item, i)
    ema5A10.count(item, i)
    ema5B10.count(item, i)
    ema5A20.count(item, i)
    ema5B20.count(item, i)
    ema10A20.count(item, i)
    ema10B20.count(item, i)
    ema5W10A20.count(item, i)
    ema5W10B20.count(item, i)
    ema5A10A20.count(item, i)
    ema5B10B20.count(item, i)
    lastNetChangeRatioUp.countLast(item, i)
    lastNetChangeRatioDown.countLast(item, i)
    lastMacdUp.countLast(item, i)
    lastMacdDown.countLast(item, i)
    lastKdjUp.countLast(item, i)
    lastKdjDown.countLast(item, i)
    lastEma5A10.countLast(item, i)
    lastEma5B10.countLast(item, i)
    lastEma5A20.countLast(item, i)
    lastEma5B20.countLast(item, i)
    lastEma5W10A20.countLast(item, i)
    lastEma5A10A20.countLast(item, i)
    lastEma5W10B20.countLast(item, i)
    lastEma5B10B20.countLast(item, i)
  }
  return {
    netChangeRatioUpDays: netChangeRatioUp.days,
    netChangeRatioUpRate: netChangeRatioUp.countRate(),
    netChangeRatioDownDays: netChangeRatioDown.days,
    netChangeRatioDownRate: netChangeRatioDown.countRate(),
    kdjUpDays: kdjUp.days,
    kdjUpRate: kdjUp.countRate(),
    kdjUpBSRate: kdjUp.countBuySellRate(),
    kdjUpKeepInfo: kdjUp.getSumInfo(),
    kdjDownDays: kdjDown.days,
    kdjDownRate: kdjDown.countRate(),
    kdjDownBSRate: kdjDown.countBuySellRate(),
    kdjDownKeepInfo: kdjDown.getSumInfo(),
    macdUpDays: macdUp.days,
    macdUpRate: macdUp.countRate(),
    macdUpBSRate: macdUp.countBuySellRate(),
    macdUpKeepInfo: macdUp.getSumInfo(),
    macdDownDays: macdDown.days,
    macdDownRate: macdDown.countRate(),
    macdDownBSRate: macdDown.countBuySellRate(),
    macdDownKeepInfo: macdDown.getSumInfo(),
    macdIncreaseDays: macdIncrease.days,
    macdIncreaseRate: macdIncrease.countRate(),
    macdIncreaseBSRate: macdIncrease.countBuySellRate(),
    macdIncreaseKeepInfo: macdIncrease.getSumInfo(),
    macdDeclineDays: macdDecline.days,
    macdDeclineRate: macdDecline.countRate(),
    macdDeclineBSRate: macdDecline.countBuySellRate(),
    macdDeclineKeepInfo: macdDecline.getSumInfo(),
    ema5A10Days: ema5A10.days,
    ema5A10Rate: ema5A10.countRate(),
    ema5A10BSRate: ema5A10.countBuySellRate(),
    ema5A10KeepInfo: ema5A10.getSumInfo(),
    ema5B10Days: ema5B10.days,
    ema5B10Rate: ema5B10.countRate(),
    ema5B10BSRate: ema5B10.countBuySellRate(),
    ema5B10KeepInfo: ema5B10.getSumInfo(),
    ema5A20Days: ema5A20.days,
    ema5A20Rate: ema5A20.countRate(),
    ema5A20BSRate: ema5A20.countBuySellRate(),
    ema5A20KeepInfo: ema5A20.getSumInfo(),
    ema5B20Days: ema5B20.days,
    ema5B20Rate: ema5B20.countRate(),
    ema5B20BSRate: ema5B20.countBuySellRate(),
    ema5B20KeepInfo: ema5B20.getSumInfo(),
    ema10A20Days: ema10A20.days,
    ema10A20Rate: ema10A20.countRate(),
    ema10A20BSRate: ema10A20.countBuySellRate(),
    ema10A20KeepInfo: ema10A20.getSumInfo(),
    ema10B20Days: ema10B20.days,
    ema10B20Rate: ema10B20.countRate(),
    ema10B20BSRate: ema10B20.countBuySellRate(),
    ema10B20KeepInfo: ema10B20.getSumInfo(),
    ema5W10A20Days: ema5W10A20.days,
    ema5W10A20Rate: ema5W10A20.countRate(),
    ema5W10A20BSRate: ema5W10A20.countBuySellRate(),
    ema5W10A20KeepInfo: ema5W10A20.getSumInfo(),
    ema5W10B20Days: ema5W10B20.days,
    ema5W10B20Rate: ema5W10B20.countRate(),
    ema5W10B20BSRate: ema5W10B20.countBuySellRate(),
    ema5W10B20KeepInfo: ema5W10B20.getSumInfo(),
    ema5A10A20Days: ema5A10A20.days,
    ema5A10A20Rate: ema5A10A20.countRate(),
    ema5A10A20BSRate: ema5A10A20.countBuySellRate(),
    ema5A10A20KeepInfo: ema5A10A20.getSumInfo(),
    ema5B10B20Days: ema5B10B20.days,
    ema5B10B20Rate: ema5B10B20.countRate(),
    ema5B10B20BSRate: ema5B10B20.countBuySellRate(),
    ema5B10B20KeepInfo: ema5B10B20.getSumInfo(),
    lastNetChangeRatioUpDays: lastNetChangeRatioUp.days,
    lastNetChangeRatioUpRate: lastNetChangeRatioUp.countRate(),
    lastNetChangeRatioDownDays: lastNetChangeRatioDown.days,
    lastNetChangeRatioDownRate: lastNetChangeRatioDown.countRate(),
    lastMacdUpDays: lastMacdUp.days,
    lastMacdUpRate: lastMacdUp.countRate(),
    lastMacdUpBSRate: lastMacdUp.countBuySellRate(),
    lastMacdUpKeepInfo: lastMacdUp.getSumInfo(),
    lastMacdDownDays: lastMacdDown.days,
    lastMacdDownRate: lastMacdDown.countRate(),
    lastMacdDownBSRate: lastMacdDown.countBuySellRate(),
    lastMacdDownKeepInfo: lastMacdDown.getSumInfo(),
    lastKdjUpDays: lastKdjUp.days,
    lastKdjUpRate: lastKdjUp.countRate(),
    lastKdjUpBSRate: lastKdjUp.countBuySellRate(),
    lastKdjUpKeepInfo: lastKdjUp.getSumInfo(),
    lastKdjDownDays: lastKdjDown.days,
    lastKdjDownRate: lastKdjDown.countRate(),
    lastKdjDownBSRate: lastKdjDown.countBuySellRate(),
    lastKdjDownKeepInfo: lastKdjDown.getSumInfo(),
    lastEma5A10Days: lastEma5A10.days,
    lastEma5A10Rate: lastEma5A10.countRate(),
    lastEma5A10BSRate: lastEma5A10.countBuySellRate(),
    lastEma5A10KeepInfo: lastEma5A10.getSumInfo(),
    lastEma5B10Days: lastEma5B10.days,
    lastEma5B10Rate: lastEma5B10.countRate(),
    lastEma5B10BSRate: lastEma5B10.countBuySellRate(),
    lastEma5B10KeepInfo: lastEma5B10.getSumInfo(),
    lastEma5A20Days: lastEma5A20.days,
    lastEma5A20Rate: lastEma5A20.countRate(),
    lastEma5A20BSRate: lastEma5A20.countBuySellRate(),
    lastEma5A20KeepInfo: lastEma5A20.getSumInfo(),
    lastEma5B20Days: lastEma5B20.days,
    lastEma5B20Rate: lastEma5B20.countRate(),
    lastEma5B20BSRate: lastEma5B20.countBuySellRate(),
    lastEma5B20KeepInfo: lastEma5B20.getSumInfo(),
    lastEma5W10A20Days: lastEma5W10A20.days,
    lastEma5W10A20Rate: lastEma5W10A20.countRate(),
    lastEma5W10A20BSRate: lastEma5W10A20.countBuySellRate(),
    lastEma5W10A20KeepInfo: lastEma5W10A20.getSumInfo(),
    lastEma5W10B20Days: lastEma5W10B20.days,
    lastEma5W10B20Rate: lastEma5W10B20.countRate(),
    lastEma5W10B20BSRate: lastEma5W10B20.countBuySellRate(),
    lastEma5W10B20KeepInfo: lastEma5W10B20.getSumInfo(),
    lastEma5A10A20Days: lastEma5A10A20.days,
    lastEma5A10A20Rate: lastEma5A10A20.countRate(),
    lastEma5A10A20BSRate: lastEma5A10A20.countBuySellRate(),
    lastEma5A10A20KeepInfo: lastEma5A10A20.getSumInfo(),
    lastEma5B10B20Days: lastEma5B10B20.days,
    lastEma5B10B20Rate: lastEma5B10B20.countRate(),
    lastEma5B10B20BSRate: lastEma5B10B20.countBuySellRate(),
    lastEma5B10B20KeepInfo: lastEma5B10B20.getSumInfo()
  }
}
